package com.wxtools.api;

import java.io.IOException;
import java.util.Map;

import net.sf.json.JSONObject;

import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.stereotype.Component;
import com.wxtools.bean.MiniJsCode2SessionResult;
import com.wxtools.bean.WxAccessToken;
import com.wxtools.bean.WxUserList;
import com.wxtools.bean.result.WxError;
import com.wxtools.bean.result.WxOAuth2AccessTokenResult;
import com.wxtools.exception.WxErrorException;
import com.wxtools.util.http.QrCodeDownloadPostRequestExecutor;
import com.wxtools.util.http.RequestExecutor;
import com.wxtools.util.http.SimpleGetRequestExecutor;
import com.wxtools.util.http.SimplePostRequestExecutor;

@Component("miniService")
public class MiniService implements IMiniService{

    //全局的是否正在刷新access token的锁
    protected static final Object globalAccessTokenRefreshLock = new Object();
    //全局的是否正在刷新jsapi_ticket的锁
    protected static final Object globalJsapiTicketRefreshLock = new Object();
    //HttpClient
    protected CloseableHttpClient httpClient;

    /**
     * 构造方法，初始化httpClient
     */
    public MiniService() {
        httpClient = HttpClients.createDefault();
    }

    @Override
    public String getAccessToken() throws WxErrorException {
        return getAccessToken(false);
    }

    @Override
    public String getAccessToken(boolean forceRefresh) throws WxErrorException {
        if (forceRefresh) {
            MiniConfig.getInstance().expireAccessToken();
        }
        if (MiniConfig.getInstance().isAccessTokenExpired()) {
            synchronized (globalAccessTokenRefreshLock) {
                if (MiniConfig.getInstance().isAccessTokenExpired()) {
                    String url = WxConsts.URL_GET_ACCESSTOEKN.replace("APPID", MiniConfig.getInstance().getAppId())
                            .replace("APPSECRET", MiniConfig.getInstance().getAppSecret());
                    try {
                        String resultContent = get(url, null);
                        WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
                        MiniConfig.getInstance().updateAccessToken(accessToken.getAccess_token(), accessToken.getExpires_in());
                        System.out.println("[wx-tools]update accessToken success. accessToken:"+accessToken.getAccess_token());
                    } catch (IOException e) {
                        throw new WxErrorException("[wx-tools]refresh accessToken failure.");
                    }
                }
            }
        }
        return MiniConfig.getInstance().getAccessToken();
    }

    @Override
    public WxUserList.WxUser getUserInfoByOpenId(WxUserList.WxUser.WxUserGet userGet) throws WxErrorException {
        WxUserList.WxUser user = null;
       /* String url = WxConsts.URL_GET_USER_INFO.replace("ACCESS_TOKEN", getAccessToken())
                .replace("OPENID", userGet.getOpenid()).replace("zh_CN", userGet.getLang());*/
        String url = WxConsts.URL_GET_USER_INFO.replace("ACCESS_TOKEN", getAccessToken())
                .replace("OPENID", userGet.getOpenid());
        String postResult = post(url, null);
        try {
            user = WxUserList.WxUser.fromJson(postResult);
        } catch (IOException e) {
            throw new WxErrorException("[wx-tools]getUserInfoByOpenId failure.");
        }
        return user;
    }

    @Override
    public MiniJsCode2SessionResult jsCode2SessionResult(String code) throws WxErrorException {
        MiniJsCode2SessionResult result = null;
        String url = WxConsts.URL_MINI_JSCODE2SESSION.replace("APPID", MiniConfig.getInstance().getAppId())
                .replace("SECRET", MiniConfig.getInstance().getAppSecret()).replace("JSCODE", code);
        String getResult = get(url, null);

        try {
            result = MiniJsCode2SessionResult.fromJson(getResult);
        } catch (IOException e) {
            throw new WxErrorException("[wx-tools]oauth2ToGetAccessToken failure.");
        }
        return result;
    }

    @Override
    public WxOAuth2AccessTokenResult oauth2ToGetRefreshAccessToken(String refresh_token) throws WxErrorException {
        WxOAuth2AccessTokenResult result = null;
        String url = WxConsts.URL_OAUTH2_GET_REFRESH_ACCESSTOKEN.replace("APPID", MiniConfig.getInstance().getAppId())
                .replace("REFRESH_TOKEN", refresh_token);
        String getResult = get(url, null);
        try {
            result = WxOAuth2AccessTokenResult.fromJson(getResult);
        } catch (IOException e) {
            throw new WxErrorException("[wx-tools]oauth2ToGetRefreshAccessToken failure.");
        }
        return result;
    }

    @Override
    public WxUserList.WxUser oauth2ToGetUserInfo(String access_token, WxUserList.WxUser.WxUserGet userGet) throws WxErrorException {
        WxUserList.WxUser user = null;
        String url = WxConsts.URL_OAUTH2_GET_USER_INFO.replace("ACCESS_TOKEN", access_token)
                .replace("OPENID", userGet.getOpenid()).replace("zh_CN", userGet.getLang());
        String getResult = get(url, null);
        try {
            user = WxUserList.WxUser.fromJson(getResult);
        } catch (IOException e) {
            throw new WxErrorException("[wx-tools]oauth2ToGetUserInfo failure.");
        }
        return user;
    }

    @Override
    public WxError oauth2CheckAccessToken(String access_token, String openid) throws WxErrorException {
        WxError err = null;
        String url = WxConsts.URL_OAUTH2_CHECK_ACCESSTOKEN.replace("ACCESS_TOKEN", access_token).replace("OPENID",
                openid);
        String getResult = get(url, null);
        try {
            err = WxError.fromJson(getResult);
        } catch (IOException e) {
            throw new WxErrorException("[wx-tools]oauth2CheckAccessToken failure.");
        }
        return err;
    }

    @Override
    public String getwxacode(String path,String width,boolean auto_color,Object line_color) throws WxErrorException {
        WxError err = null;
        String url = WxConsts.URL_GET_WXACODE.replace("ACCESS_TOKEN", getAccessToken());
        JSONObject params=new JSONObject();
        params.put("path",path);
        params.put("width",width);
        params.put("auto_color",auto_color);
        params.put("line_color",line_color);
        return post(url, params.toString());
    }

    @Override
    public String createwxaqrcode(String path,String width) throws WxErrorException {

        String url = WxConsts.URL_CREATE_WXAQRCODE.replace("ACCESS_TOKEN", getAccessToken());
        String params="{\"path\":\""+path +"\",\"width\":\""+width+"\"}";
        /*String params
        JSONObject params=new JSONObject();
        params.put("path",path);
        params.put("width",width);*/
        return postAndDownload(url, params);
    }

    public String get(String url, Map<String, String> params) throws WxErrorException {
        return execute(new SimpleGetRequestExecutor(), url, params);
    }

    public String post(String url, String params) throws WxErrorException {
        return execute(new SimplePostRequestExecutor(), url, params);
    }
    
    public String postAndDownload(String url, String params) throws WxErrorException {
   
        return execute(new QrCodeDownloadPostRequestExecutor(), url, params);
    }

    //QrCodeDownloadGetRequestExecutor

    @Override
    public <T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException {
        try {
            return executeInternal(executor, uri, data);
        } catch (WxErrorException e) {
            throw e;
        }
    }

    /**
     * 请求执行器
     *
     * @param executor
     * @param uri
     * @param data
     * @return
     * @throws WxErrorException
     * @throws IOException
     */
    protected synchronized <T, E> T executeInternal(RequestExecutor<T, E> executor, String uri, E data)
            throws WxErrorException{
        try {
            return executor.execute(getHttpclient(), uri, data);
        } catch (WxErrorException e) {
            throw e;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    protected CloseableHttpClient getHttpclient() {
        return this.httpClient;
    }
    
}
